package com.gupao.pattern.proxy.dynamicproxy.jdk.custom;

import com.gupao.pattern.proxy.Person;
import com.gupao.pattern.proxy.dynamicproxy.jdk.Girl;
import com.gupao.pattern.proxy.dynamicproxy.jdk.MeiPoProxy;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.List;

public class GPProxy {

    private static String ln = "\r\n";


    public static Object newProxyInstance(GPClassLoader gpClassLoader, Class<?>[] interfaces, GPInvocationHandler h) {

        try {
            //1.动态生成java文件
            String src = generateSrc(interfaces);
            System.out.println(src);
            //2.把java文件写入磁盘
            String filePath = GPProxy.class.getResource("").getPath();
            File file = new File(filePath + "$Proxy0.java");
            FileWriter fw = new FileWriter(file);
            fw.write(src);
            fw.flush();
            fw.close();
            //3.解析java文件，编译成class文件
            JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(null, null, null);
            Iterable<? extends JavaFileObject> iterable = fileManager.getJavaFileObjects(file);
            JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager, null, null, null, iterable);
            task.call();
            fileManager.close();
            //4.把class文件加载到JVM虚拟机运行
            Class<?> $Proxy0 = gpClassLoader.findClass("$Proxy0");
            //5.反射拿到构造函数,并且实例化对象
            Constructor<?> constructor = $Proxy0.getConstructor(GPInvocationHandler.class);
            Object obj = constructor.newInstance(h);
            //6.删除java文件
            file.delete();
            return obj;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    private static String generateSrc(Class<?>[] interfaces) {

        StringBuffer sb = new StringBuffer();

        sb.append("package com.gupao.pattern.proxy.dynamicproxy.jdk.custom;" + ln);
        sb.append("import java.lang.reflect.Method;" + ln);
        sb.append("import com.gupao.pattern.proxy.Person;" + ln);

        sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
        sb.append("private GPInvocationHandler h;" + ln);

        sb.append("public $Proxy0(GPInvocationHandler h){" + ln);
        sb.append("this.h=h;");
        sb.append("}" + ln);

        for (Method method : interfaces[0].getMethods()) {

            Class<?>[] params = method.getParameterTypes();

            //拼接方法参数
            StringBuffer paramNames = new StringBuffer();
            StringBuffer paramValues = new StringBuffer();
            StringBuffer paramClasses = new StringBuffer();

            for (int i = 0; i < params.length; i++) {
                Class<?> clazz = params[i];
                String type = clazz.getName();
                String paramName = "arg" + i;
                paramValues.append(paramName);
                paramNames.append(type + " " + paramName);
                paramClasses.append(clazz.getName() + ".class");
                if (i >= 0 && i < params.length - 1) {
                    paramNames.append(",");
                    paramClasses.append(",");
                    paramValues.append(",");
                }
            }


            paramClasses = paramClasses.length() < 1 ? null : paramClasses;
            paramValues = paramValues.length() < 1 ? null : paramValues;


            sb.append("public " + method.getReturnType().getName() + " " + method.getName() + "(" + paramNames.toString() + "){" + ln);
            sb.append("try{" + ln);
            sb.append("Method method=" + interfaces[0].getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{" + paramClasses + "});" + ln);

            String returnString = "void".equals(method.getReturnType().getName()) ? "return" : "return (String)this.h.invoke(this,method,new Object[]{" + paramValues + "});";
            sb.append(returnString + ln);
            sb.append("}catch(Throwable e){" + ln);
            sb.append("e.printStackTrace();" + ln);
            sb.append("}" + ln);
            sb.append("return null;" + ln);
            sb.append("}" + ln);

        }

        sb.append("}" + ln);


        return sb.toString();
    }


}
